﻿using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace PengSW.ReflectionHelper
{
    public static class ReflectionHelper
    {
        public static object GetPropertyValue(this object aObj, string aPropertyName)
        {
            return aObj?.GetType()?.GetProperty(aPropertyName)?.GetValue(aObj);
        }

        public static void SetPropertyValue(this object aObj, PropertyInfo aProperty, object aPropertyValue)
        {
            if (aObj == null) return;
            if (aProperty.SetMethod == null) throw new ApplicationException($"Property [{aProperty.Name}] can not be set.");
            if (aPropertyValue == null)
            {
                aProperty.SetValue(aObj, null);
            }
            else if (aProperty.PropertyType == aPropertyValue.GetType())
            {
                aProperty.SetValue(aObj, aPropertyValue);
            }
            else if (aProperty.PropertyType.IsEnum)
            {
                object aValue = Enum.Parse(aProperty.PropertyType, aPropertyValue.ToString());
                aProperty.SetValue(aObj, aValue);
            }
            else
            {
                object aValue;
                if (aProperty.PropertyType.IsGenericType && Regex.IsMatch(aProperty.PropertyType.Name, "^Nullable`"))
                {
                    aValue = Convert.ChangeType(aPropertyValue, aProperty.PropertyType.GenericTypeArguments.First());
                }
                else
                {
                    aValue = Convert.ChangeType(aPropertyValue, aProperty.PropertyType);
                }
                aProperty.SetValue(aObj, aValue);
            }
        }

        public static void SetPropertyValue(this object aObj, string aPropertyName, object aPropertyValue)
        {
            if (aObj == null) return;
            var aProperty = aObj.GetType()?.GetProperty(aPropertyName);
            if (aProperty == null) throw new ApplicationException($"Property [{aPropertyName}] not found.");
            SetPropertyValue(aObj, aProperty, aPropertyValue);
        }

        public static bool HasAttribute(this object aObject, string aPropertyName, Type aAttributeType)
        {
            if (aObject == null) return false;
            return HasAttribute(aObject.GetType().GetProperty(aPropertyName), aAttributeType);
        }

        public static bool HasAttribute(this PropertyInfo aPropertyInfo, Type aAttributeType)
        {
            if (aPropertyInfo == null) return false;
            return aPropertyInfo.GetCustomAttribute(aAttributeType) != null;
        }

        public static PropertyInfo[] GetPropertiesWithAttribute(this Type aType, Type aAttriuteType)
        {
            return (from r in aType.GetProperties() where r.HasAttribute(aAttriuteType) select r).ToArray();
        }

        public static PropertyInfo[] GetPropertiesWithInterface(this Type aType, Type aInterfaceType)
        {
            return (from r in aType.GetProperties() where r.PropertyType.GetInterface(aInterfaceType.Name) == aInterfaceType select r).ToArray();
        }
    }
}
